
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include "lib.h"
#include "nvpairs.h"

///////////////////////////////////////////////////////////////
// Global Helper Functions
///////////////////////////////////////////////////////////////

static char
hexToChar(char *hex)
{
	char tempHex;
	char retChar;

	tempHex = toupper(*hex);
	if (tempHex >= '0' && tempHex <= '9')
		retChar = 16*(tempHex-'0');
	else if (tempHex >= 'A' && tempHex <= 'F')
		retChar = 16*(tempHex-'A'+10);
	else
		retChar = 0;

	tempHex = toupper(*(hex+1));
	if (tempHex >= '0' && tempHex <= '9')
		retChar += tempHex-'0';
	else if (tempHex >= 'A' && tempHex <= 'F')
		retChar += tempHex-'A'+10;

	return retChar;
}


/************************************************************************************************
*
* Name-value pair (one pair)
*
*************************************************************************************************/

nvPair::nvPair(const String& name, const String& value)
{
	this->name = name;
	this->value = value;
}

nvPair::~nvPair()
{
}

/************************************************************************************************
*
* Name-value pairs - array of name/value pairs
*
*************************************************************************************************/


nvPairs::nvPairs()
{
	m_numPairs = 0;
	m_delimiter='&';  // default delimiter
}



nvPairs::nvPairs (const char* formInput)
{
	m_numPairs = 0;
	m_delimiter='&';  // default delimiter
	setFormInput(formInput);
}



nvPairs::~nvPairs()
{
	uint32 i;
	for (i=0; i < m_numPairs; i++)
		delete m_pairs[i];
}



void
nvPairs::addPair(nvPair *pair)
{
	if (m_numPairs < MAX_NVPAIRS - 1)
		m_pairs[m_numPairs++] = pair;
}

uint32
nvPairs::numPairs() const
{
	return m_numPairs;
}

bool8
nvPairs::getPairAt(uint32 at, String& name, String& value) const
{
	nvPair *	pair;

	pair = m_pairs[at];
	if (!pair)
		return false;

	name	= pair->getName();
	value	= urlDecode(pair->getValue());

	return true;
}

char*
nvPairs::urlEncode(const String& url)
{
	//max size of utf-8 is 6 characters per unicode char
	uint32 utf8len = url.getLength() * 6 + 1;
	char* utf8url = (char*)malloc(utf8len);

	if (!utf8url)
		return 0;

	*utf8url = '\0';

	StringConversions::encodeUTF8(utf8url, url, utf8len);
	utf8len = strlen(utf8url);

	char* safeurl = (char*)malloc(3*utf8len+1); // maximum length is 3 times original

	//URL Encoding replaces spaces with "+" signs,
	//and unsafe ASCII characters with "%" followed by their hex equivalent.
	//Safe characters are defined in RFC2396.
	//They are the 7-bit ASCII alphanumerics and the mark characters "-_.!~*'()".
	for(uint32 i=0,j=0;i<utf8len;i++)
	{
		char c = utf8url[i];

		if (c == ' ')
		{
			safeurl[j++] = '+';
		}
		//7-bit ascii alphanumerics == safe
		else if (
			(c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
			(c >= '0' && c <= '9') || c == '-' || c == '_' ||
			c == '.' || c == '!' ||	c == '~' || c == '*' ||
			c == '\'' || c== '(' ||	c == ')' || c == '<' || c == '>'
			)
		{
			safeurl[j++] = c;
		}
		else
		{
			char szHex[4] = { '%', '\0' };

			itoa(c & 0x000000FF, szHex + 1, 16);
	      	strcpy(safeurl + j, szHex);
		  	j += 3;
		}
	}
	safeurl[j] = '\0';

	free(utf8url);
	utf8url = 0;

	return safeurl;
}



String
nvPairs::urlDecode(const String& url)
{
	char buffer[2048];

	StringConversions::convert(buffer, url, 2048);

	return urlDecode(buffer);
}


String
nvPairs::urlDecode(const char* inurl)
{
	int i,j;

	char* url = strdup(inurl);

	if (!url)
		return "";

	for(i = 0, j = 0; url[j]; i++, j++)
  	{
		if ((url[i] = url[j]) == '+')
		{
			url[i] = ' ';
		}
		else if ((url[i] = url[j]) == '%')
		{
			url[i] = hexToChar(&url[j+1]);
			j += 2;
		}
  	}

	url[i] = '\0';

	String result = "";

	int32 len = strlen(url) + 1;
	wchar_t* buffer = result.getBuffer( len );

	StringConversions::decodeUTF8(buffer, url, len);

	result.releaseBuffer();

	free(url);
	url = 0;

	return result;
}



String
nvPairs::getValue (const String& name) const
{
	uint32 i;

	for (i=0; i < m_numPairs; i++)
	{
		if (m_pairs[i]->getName() == name)
			return m_pairs[i]->getValue();
	}

	return ("");
}



char *
nvPairs::getURLEncodedString() const
{
	uint32 i;
	char *returnstring, *name, *value;

	/*** Check for empty or invalid struct ***/
	if (m_numPairs == 0)
		return (strdup(""));

	/**** Build return string *****/
	returnstring=strdup("");
	for (i=0; i < m_numPairs; i++)
	{
		if (m_pairs[i]->getName())
			name = urlEncode(m_pairs[i]->getName());
		else
			name = strdup("");

		if (m_pairs[i]->getValue())
			value = urlEncode(m_pairs[i]->getValue());
		else
			value = strdup("");

		returnstring = (char *)realloc(returnstring, strlen(returnstring) + strlen(name) + strlen(value) + 3);
		strcat(returnstring, name);
		strcat(returnstring, "=");
		strcat(returnstring, value);
		if (i + 1 < m_numPairs)
			strcat(returnstring, "&");

		free(name);
		free(value);
	}

	return returnstring;
}



void
nvPairs::setNVDelimiter(char delimiter)
{
	m_delimiter = delimiter;
}

void
nvPairs::setFormInput(const char * formInput)
{
	const char *	src = formInput;
	char *	dst;
	char *	name;
	char *	value;

	// Null pointers are not welcomed here!
	if (!formInput)
		return;

	// Allocate enough memory for a worst-case situation.
	name  = (char *)malloc(strlen(formInput) + 1);
	value = (char *)malloc(strlen(formInput) + 1);

	// On allocation failure, sneak past the while statement!
	if (!name || !value)
		src = 0;

	// Loop through formInput (one name-value pair at a time) parsing out
	// the name from the value.
	while (src && *src)
	{
		// Initialize each iteration (important for error checking - see
		// table below).
		*name  = 0;
		*value = 0;

		// Extract name.
		for (dst = name; *src && *src != m_delimiter && *src != '='; /*empty*/)
			*dst++ = *src++;

		// Terminate name.
		*dst = '\0';

		// Extract optional value.
		if (*src == '=')
		{
			src++;

			for (dst = value; *src && *src != m_delimiter; /*empty*/)
				*dst++ = *src++;

			// Terminate value.
			*dst = '\0';
		}

		if (*src == m_delimiter)
			src++;

		// At this point we have parsed one set of name-value pairs from
		// formInput yielding one of these possible situations:
		//
		//                  local variables
		//                 /       |       \
		//       formInput     |  name   |  value   |  well-formed  |
		//     ================+=========+==========+===============+
		//       aName=aValue  |  aName  |  aValue  |      yes      |
		//     ----------------+---------+----------+---------------+
		//       aName=        |  aName  |   \0     |      yes      |
		//     ----------------+---------+----------+---------------+
		//       aName         |  aName  |   \0     |      yes      |
		//     ----------------+---------+----------+---------------+
		//       =aValue       |   \0    |  aValue  |      no       |
		//     ----------------+---------+----------+---------------+
		//
		// So ... make sure we only add well-formed name-value pairs.
		//
		if (*name)
			addPair(new nvPair(urlDecode(name), urlDecode(value)));
	}

	// Clean up.
	if (name)
		free(name);
	if (value)
		free(value);
}

void
nvPairs::setFormInput(const String& formInput)
{
	const wchar_t *	src = formInput;
	wchar_t *	dst;
	wchar_t *	name;
	wchar_t *	value;

	// Null pointers are not welcomed here!
	if (!formInput)
		return;

	// Allocate enough memory for a worst-case situation.
	name  = (wchar_t *)malloc((wcslen(formInput) + 1) * sizeof(wchar_t));
	value = (wchar_t *)malloc((wcslen(formInput) + 1) * sizeof(wchar_t));

	// On allocation failure, sneak past the while statement!
	if (!name || !value)
		src = 0;

	// Loop through formInput (one name-value pair at a time) parsing out
	// the name from the value.
	while (src && *src)
	{
		// Initialize each iteration (important for error checking - see
		// table below).
		*name  = 0;
		*value = 0;

		// Extract name.
		for (dst = name; *src && *src != m_delimiter && *src != '='; /*empty*/)
			*dst++ = *src++;

		// Terminate name.
		*dst = '\0';

		// Extract optional value.
		if (*src == '=')
		{
			src++;

			for (dst = value; *src && *src != m_delimiter; /*empty*/)
				*dst++ = *src++;

			// Terminate value.
			*dst = '\0';
		}

		if (*src == m_delimiter)
			src++;

		// At this point we have parsed one set of name-value pairs from
		// formInput yielding one of these possible situations:
		//
		//                  local variables
		//                 /       |       \
		//       formInput     |  name   |  value   |  well-formed  |
		//     ================+=========+==========+===============+
		//       aName=aValue  |  aName  |  aValue  |      yes      |
		//     ----------------+---------+----------+---------------+
		//       aName=        |  aName  |   \0     |      yes      |
		//     ----------------+---------+----------+---------------+
		//       aName         |  aName  |   \0     |      yes      |
		//     ----------------+---------+----------+---------------+
		//       =aValue       |   \0    |  aValue  |      no       |
		//     ----------------+---------+----------+---------------+
		//
		// So ... make sure we only add well-formed name-value pairs.
		//
		if (*name)
			addPair(new nvPair(name, value));
	}

	// Clean up.
	if (name)
		free(name);
	if (value)
		free(value);
}


